#include "Entity.h"
#include "ExecutionLog.h"
#include "ClippingPlane.h"
#include "Entity Manager.h"
#include <stdio.h>
#include <fstream>
#include "Globals.hpp"
#include "GameWorld.h"
#include "General Utils.hpp"

using namespace std;

int Entity::ignoredImage = 1;


float Entity::m_z = 0.0f;
float Entity::m_scaler = 1;
int Entity::m_entityCount = 0;

vector<GLuint> Entity::m_images;
vector<GLuint> Entity::m_displayLists;

vector<Dimension> Entity::m_images_sizes_padded;
vector<Dimension> Entity::m_images_sizes_orig;
vector<string> Entity::m_images_names;

//map<string, vector<Entity::SubBase> > Entity::m_GetSubBaseFromBase;
//map<string, int> Entity::NameToIDMap;


Timer Entity::GameTimer = Timer();



/*      --------------Default Constructor----------------
        Simply sets the screen coordinates to 0,0. and calls
        the default constructor of the TGAImg for sourceImg */
Entity::Entity()
{
	Rect.x = 0;
    Rect.y = 0;

    Rect.h = 0;
    Rect.w = 0;
    
    imageID = 0;

	// Animation
	m_animationFrames.reserve(5);
	m_currentFrame = 0;
	m_isAnimation = false;

	m_animationAngle = 0;

	m_scale = 1.0;

	m_entityCount++;

	//m_remaingLoopsUntilUpdate = LOOPS_UNTIL_NEXT_ANIMATION;
}


// Given, a string "name" of an Entity, it will use the NameToMapID to find the ID of its image.
Entity::Entity(string name)
{
    
    //imageID = NameToIDMap[name];
	imageID = ImageManager->GetImageID(name);

	Rect.x = 0;
    Rect.y = 0;
    Rect.h = m_images_sizes_orig[imageID].h;	
	Rect.w = m_images_sizes_orig[imageID].w;

	// Animation
	m_animationFrames.reserve(5);
	m_currentFrame = 0;
	m_isAnimation = false;

	m_animationAngle = 0;

	m_scale = 1.0;

	m_entityCount++;


}


/*      ---------------Default Destructor------------------
        Simply calls the default destructor of TGAImg for
        sourceImg. */

Entity::~Entity()
{
	//--m_entityCount;
        //DestroyAllImages(); // ends up getting called about 8000 times...
}

/*      -----------------------applySurface-----------------------------
        Displays the image*/
void Entity::applySurface()
{

	//assert("Image Not Found." && m_displayLists[imageID]);

    if(imageID == ignoredImage) // optimization; don't draw something that isn't going to show up!
            return;                                 // i figure an equality check is slightly faster :P (don't have to change any values)


	SDL_Rect aRect(Rect);

	aRect.x *= m_scaler;
	aRect.y *= m_scaler;
	aRect.w *= m_scaler;
	aRect.h *= m_scaler;

	if(m_isAnimation)
		SetNextImage();

	if(ClipPlane->IsInPlane(aRect))
	{
		glTranslatef(Rect.x - ClipPlane->GetX(), Rect.y - ClipPlane->GetY(), m_z);
	
		glTranslatef((Rect.w / 2), (Rect.h / 2), 0.0f);
		glRotatef(m_animationAngle,  0.0f, 0.0f, 1.0f);
		glTranslatef(-(Rect.w / 2), -(Rect.h / 2), 0.0f);

		glScalef(m_scaler, m_scaler, 1.0f);
		glCallList(m_displayLists[imageID]);	
		glLoadIdentity();
	}	
}

// renders the specified ID, if not valid it aborts
void Entity::RenderImage(int x, int y, int ID, ClippingPlane* pClippingPlane)
{

	if(ID > m_displayLists.size() || ID < 0)
		return; // invalid ID; abort the render

    if(ID == ignoredImage) // optimization; don't draw something that isn't going to show up!
		return;            // i figure an equality check is slightly faster :P (don't have to change any values)


	SDL_Rect aRect;

	aRect.x = x;
	aRect.y = y;
	aRect.w = m_images_sizes_padded[ID].w;
	aRect.h = m_images_sizes_padded[ID].h;

	if(pClippingPlane->IsInPlane(aRect))
	{
		glTranslatef(aRect.x - pClippingPlane->GetX(), aRect.y - pClippingPlane->GetY(), m_z);
		glCallList(m_displayLists[ID]);	
		glLoadIdentity();
	}	
}

void Entity::applySurface(float rotation)
{
    assert("Image Not Found." && m_displayLists[imageID]);

    if(imageID == ignoredImage) // optimization; don't draw something that isn't going to show up!
		return;                                 // i figure an equality check is slightly faster :P (don't have to change any values)


	SDL_Rect aRect(Rect);

	aRect.x *= m_scaler;
	aRect.y *= m_scaler;
	aRect.w *= m_scaler;
	aRect.h *= m_scaler;

	if(ClipPlane->IsInPlane(aRect))
	{
		glScalef(m_scale, m_scale, 1.0f);	

		glTranslatef(Rect.x - ClipPlane->GetX(), Rect.y - ClipPlane->GetY(), m_z);

		glTranslatef((Rect.w / 2), (Rect.h / 2), 0.0f);
		glRotatef(rotation,  0.0f, 0.0f, 1.0f);
		glTranslatef(-(Rect.w / 2), -(Rect.h / 2), 0.0f);


		glCallList(m_displayLists[imageID]);
		glLoadIdentity();
	}
}

// Returns if the entity is on the screen
bool Entity::OnScreen()
{
	return ClipPlane->IsInPlane(Rect);
}

void Entity::FreeImage(int imgID)
{
	if(m_images[imgID])
	{
		glDeleteTextures(1, &m_images[imgID]);
        m_images[imgID] = NULL; // Nobody likes a dangling pointer.

		glDeleteLists(m_displayLists[imgID], 1);
		m_displayLists[imgID] = NULL; // Nobody loves a dangling pointer.
	}
}





/*      ----------------------------SetID-----------------------------
        Merely sets the ID of the current Entity */

// Initializes the Entity class. Sets the destination to the screen. Reserves space for images.
// It reads through the file "Images.txt" to set the NameToIDMap and load the images into the
// m_images vector.

void Entity::Init()
{

	GameTimer.Restart();
    m_images.reserve(20);
}

void Entity::AddAllImages()
{

	Lua->RunScript("Scripts\\Images.lua");

	vector<ImageLoader::Image> images = ImageManager->GetAllImages();
	int curr = 0;
	int totalImages = images.size();
	if(totalImages < 0)
		totalImages = 0;
	while(curr < totalImages)
	{
		// Add the images size in
		m_images_sizes_padded.resize(m_images_sizes_padded.size() + 1);
		m_images_sizes_orig.resize(m_images_sizes_orig.size() + 1);

		m_images_sizes_padded[m_images_sizes_padded.size() - 1].h = images[curr].padded.h;
		m_images_sizes_padded[m_images_sizes_padded.size() - 1].w = images[curr].padded.w;
		m_images_sizes_orig[m_images_sizes_orig.size() - 1].h = images[curr].orig.h;
		m_images_sizes_orig[m_images_sizes_orig.size() - 1].w = images[curr].orig.w;


		// Display list
		// Increase display list size
		m_displayLists.resize(m_displayLists.size() + 1);
		m_displayLists[m_displayLists.size() - 1] = glGenLists(1);

		// Build the list
		glNewList(m_displayLists[m_displayLists.size() - 1], GL_COMPILE);
			// Set color to (nothing)
			glColor4f(1.0f, 1.0f, 1.0f, 1.0f);

			// Bind the texture
			glBindTexture(GL_TEXTURE_2D, images[curr].texture);

			//SDL_Rect aRect(Rect);
			SDL_Rect aRect;
			aRect.x = 0;
			aRect.y = 0;

			aRect.x = (aRect.x - ClipPlane->GetX());
			aRect.y = (aRect.y - ClipPlane->GetY());
			aRect.w = m_images_sizes_padded[m_images_sizes_padded.size() - 1].w;
			aRect.h = m_images_sizes_padded[m_images_sizes_padded.size() - 1].h;

			glBegin(GL_QUADS);
				//Top-left vertex (corner)
				glTexCoord2i( 0, 0 );
				glVertex3f( aRect.x, aRect.y, 0.0f );
				
				//Bottom-left vertex (corner)
				glTexCoord2i( 1, 0 );
				glVertex3f( GLfloat(aRect.x + aRect.w), GLfloat(aRect.y), 0.0f );
				
				//Bottom-right vertex (corner)
				glTexCoord2i( 1, 1 );
				glVertex3f( GLfloat(aRect.x + aRect.w), GLfloat(aRect.y + aRect.h), 0.0f );
				
				//Top-right vertex (corner)
				glTexCoord2i( 0, 1 );
				glVertex3f( GLfloat(aRect.x), GLfloat(aRect.y + aRect.h), 0.0f );
			glEnd();
		glEndList();
		
		//NameToIDMap.insert(std::pair<string, int>(images[curr].name, curr));
		//NameToIDMap[images[curr].name] = curr;
		++curr;
	}


}


void Entity::FreeAllImages()
{
	for (unsigned int i = 0; i < m_images.size(); i++)
	{
		glDeleteTextures(1, &m_images[i]);
		m_images[i] = NULL;
		glDeleteLists(m_displayLists[i], 1);
		m_displayLists[i] = NULL;
	}
	ImageManager->DeleteAllTextures();
	ImageManager->DeleteAllAnimations();
	
}
     
// =========================Intersects======================
// Returns true if this entity intersects this rectangle
bool Entity::Intersects(SDL_Rect aRect)
{
	int leftA, leftB;
	int rightA, rightB;
	int topA, topB;
	int bottomA, bottomB;

	leftA = Rect.x;
	rightA = Rect.x + Rect.w;
	topA = Rect.y;
	bottomA = Rect.y + Rect.h;

	leftB = aRect.x;
	rightB = aRect.x + aRect.w;
	topB = aRect.y;
	bottomB = aRect.y + aRect.h;

	if(bottomA <= topB)
		return false;
	if(topA >= bottomB)
		return false;
	if(rightA <= leftB)
		return false;
	if(leftA >= rightB)
		return false;

	return true;
}

bool Entity::Intersects(GameRectangle aRect)
{
	GameRectangle FixedRect = GetRect(aRect.x, aRect.y, aRect.x + aRect.width, aRect.y + aRect.height);
	
	int leftA, leftB;
	int rightA, rightB;
	int topA, topB;
	int bottomA, bottomB;

	leftA = Rect.x;
	rightA = Rect.x + Rect.w;
	topA = Rect.y;
	bottomA = Rect.y + Rect.h;

	leftB = FixedRect.x;
	rightB = FixedRect.x + FixedRect.width;
	topB = FixedRect.y;
	bottomB = FixedRect.y + FixedRect.height;

	if(bottomA <= topB)
		return false;
	if(topA >= bottomB)
		return false;
	if(rightA <= leftB)
		return false;
	if(leftA >= rightB)
		return false;

	return true;
}

void Entity::DrawRect()
{
	if(ClipPlane->IsInPlane(Rect))
	{
		SDL_Rect aRect(Rect);
		aRect.x = (aRect.x - ClipPlane->GetX());
		aRect.y = (aRect.y - ClipPlane->GetY());
		DrawPrimitives->DrawRectangle(aRect.x, aRect.y, aRect.x + aRect.w, aRect.y + aRect.h, gray, 0.3f);
	}
}

void Entity::SetAnimationBase(string baseName, string subBaseName)
{
	m_isAnimation = true;

	m_BaseImg = baseName;

	SetAnimationSubBase(subBaseName);

}

void Entity::SetAnimationSubBase(string subBase)
{
	m_animationFrames.clear();
	
	for(unsigned int j = 0; j < ImageManager->GetSubBase(m_BaseImg, subBase).FrameIDs.size(); ++j) // Replace with new images
		m_animationFrames.push_back(ImageManager->GetSubBase(m_BaseImg, subBase).FrameIDs[j]);
		
	assert(m_animationFrames.size() > 0 && "m_animationFrames.size() in Entity::SetAnimationSubBase is too small.");

	imageID = m_animationFrames[0];
	m_animationUpdate = ImageManager->GetSubBase(m_BaseImg, subBase).UpdateTime;

	m_animationTimer.Restart();
}

void Entity::SetNextImage()
{
	if(m_isAnimation)
	{
		int a = m_animationTimer.GetElapsedMilliseconds() / m_animationUpdate;

		if(a >= 1)
			m_animationTimer.Restart();

		m_currentFrame += a;

		if(m_currentFrame >= m_animationFrames.size() || m_currentFrame < 0)
		{
			m_currentFrame = 0;

			imageID = m_animationFrames[0];

			return;
		}

		if(m_currentFrame >  m_animationFrames.size())
		{
			#ifdef CONSOLE_DEBUG
			cout << "Warning: m_currentFrame larger than m_animationFrames.size()!" << endl;
			#endif
			return;
		}
		imageID = m_animationFrames[m_currentFrame];		
	}
}

void Entity::SetRect(GameRectangle Rectangle)
{
	assert(Rectangle.x >= 0 && Rectangle.y >= 0 && Rectangle.width >= 0 && Rectangle.height >= 0);
	SDL_Rect SDLrect = {Rectangle.x, Rectangle.y, Rectangle.width, Rectangle.height};

	return SetRect(SDLrect);
}